package com.self.distributor;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Transaction;

import java.util.List;
import java.util.UUID;

/**
 * 作者：tianjiayuan
 * 创建时间：2018-06-11 11:02
 * 类描述：
 * 修改人：
 * 修改时间：
 */
public class DistributedLock {

    private JedisPool jedisPool;

    public DistributedLock(JedisPool jedisPool) {
        this.jedisPool = jedisPool;
    }

    /**
     * 加锁
     * @param lockName
     * @param lockTime 获取超时时间
     * @param timeout  超时时间
     * @return
     */
    public String lockWithTimeout(String lockName, int acquireTime, int timeout) {
        Jedis jedis = null;

        String retIdentifier = null;

        try {
            //获取一个连接
            jedis = jedisPool.getResource();
            String identifier = UUID.randomUUID().toString();
            String lock = "lock:" + lockName;
            int lockExpire = (int)(timeout/1000);

            //获取锁超时时间
            long end = System.currentTimeMillis() + acquireTime;
            while(System.currentTimeMillis() < end) {
                if (jedis.setnx(lock, identifier) == 1) {
                    jedis.expire(lock, lockExpire);
                    retIdentifier = identifier;
                    return retIdentifier;
                }
                if (jedis.ttl(lock) == -1) {
                    jedis.expire(lock, lockExpire);
                }
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            jedis.close();
        }
        return retIdentifier;
    }


    /**
     * 释放锁
     * @param lockName
     * @param identifier 锁标示
     * @return
     */
    public boolean releaseLock(String lockName, String identifier) {
        Jedis conn = null;
        String lockKey = "lock:" + lockName;
        boolean retFlag = false;
        try {
            conn = jedisPool.getResource();
            while (true) {
                //监视key ，准备开始事物
                conn.watch(lockKey);
                if (identifier.equals(conn.get(lockKey))) {
                    //开启事物
                    Transaction transaction = conn.multi();
                    //序列化命令
                    transaction.del(lockKey);
                    //提交命令 CAS比较
                    List<Object> result = transaction.exec();
                    if (result == null) {
                        continue;
                    }
                    retFlag = true;
                }
                conn.unwatch();
                break;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            conn.close();
        }
        return retFlag;
    }
}
